package com.dynamic.mybatis.core.builder;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.dynamic.mybatis.core.metadata.DynamicConfiguration;
import com.dynamic.mybatis.core.metadata.DynamicMappedStatement;
import com.dynamic.mybatis.core.metadata.DynamicScriptMappedStatement;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.session.Configuration;

import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description
 * @Author xs
 * @Date 2021/11/10 16:18
 */
public class DynamicMapperAdapterBuilder extends DynamicBaseMapperBuilder {

    private static String rootDocTypeXml = "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">";

    public DynamicMapperAdapterBuilder(Configuration configuration) {
        super(configuration);
    }

    public  DynamicMappedStatement parserXml(DynamicMappedStatement dms){
        String resultMap = null;
        if(dms instanceof DynamicScriptMappedStatement){
            resultMap = ((DynamicScriptMappedStatement)dms).getResultMap();
        }
        DynamicConfiguration dc = (DynamicConfiguration) configuration;
        String dynamicNamespace = dc.DYNAMIC_NAMESPACE;
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(rootDocTypeXml);
        stringBuilder.append("<mapper namespace=\""+dynamicNamespace+"\">");
        if(StringUtils.isNotBlank(resultMap)){
            stringBuilder.append(resultMap);
        }
        stringBuilder.append(dms.getMappedStatement());
        stringBuilder.append("</mapper>");
        ByteArrayInputStream byteArrayInputStream  = new ByteArrayInputStream(stringBuilder.toString().getBytes());
        Map<String, XNode> sqlFragments = new HashMap<>();
        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(byteArrayInputStream,configuration, dc.DYNAMIC_RESOURCE,sqlFragments);
        xmlMapperBuilder.parse();
        dc.addSqlFragments(sqlFragments);

        dc.addDynamicMappedStatement(dms);

        if(dc.hasMappedStatement(dms.getId())){
            dms.setSqlCommandType(configuration.getMappedStatement(dms.getId()).getSqlCommandType());
        }
        return dms;
    }


    public DynamicScriptMappedStatement parserScript(DynamicScriptMappedStatement dsms){
        //SQL解析
        SqlSource sqlSource = createSqlSource(configuration,trimBlank(dsms.getMappedStatement()),null);
        //初始化mappedStatement
        MappedStatement.Builder builder =  new MappedStatement.Builder(configuration,dsms.getId(),sqlSource,dsms.getSqlCommandType());
        MappedStatement mappedStatement = builder.resultMaps(parserResultMap(dsms.getId(),dsms.getResultType(),dsms.getResultMap())).build();
        //将mappedStatement 加入mybatis上下文中
        configuration.addMappedStatement(mappedStatement);

        DynamicConfiguration dc = ((DynamicConfiguration)configuration);
        dc.addDynamicMappedStatement(dsms);

        if(dc.hasMappedStatement(dsms.getId())){
            dsms.setSqlCommandType(configuration.getMappedStatement(dsms.getId()).getSqlCommandType());
        }

        return  dsms;
    }

    /**
     * @Description: 初始化sql对象
     * @param configuration mybatis上下文
     * @param mappedStatement 动态SQL
     * @param parameterType 参数类型
    　*/
    private   SqlSource createSqlSource(Configuration configuration,String mappedStatement,String parameterType){
        Class parameterTypeClass;
        if(StringUtils.isBlank(parameterType)){
            parameterTypeClass = HashMap.class;
        }else {
            parameterTypeClass = configuration.getTypeAliasRegistry().resolveAlias(parameterType);
        }
        return configuration.getDefaultScriptingLanguageInstance().createSqlSource(configuration,mappedStatement,parameterTypeClass);
    }

    public DynamicMappedStatement parser(DynamicMappedStatement dms){
        try {
            if(isXml(trimBlank(dms.getMappedStatement()))){
                parserXml(dms);
            }else {
                parserScript((DynamicScriptMappedStatement) dms);
            }
         }catch (Exception e){
            throw  ExceptionFactory.wrapException("mappedStatement parser is error or unknow type. mappedStatement is " + dms.getId() +" .Cause: "+e, e);
         }
        DynamicConfiguration dc =  ((DynamicConfiguration)configuration);
        if(!dc.hasMappedStatement(dms.getId())){
            dc.addDynamicMappedStatement(dms);
        }
        return dms;
    }
}